package com.example.redis_redisson_demo.web.redis;

import com.alibaba.fastjson.JSON;
import com.example.redis_redisson_demo.common.utils.R;
import com.example.redis_redisson_demo.dao.AttrDao;
import com.example.redis_redisson_demo.entity.AttrEntity;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 使用StringRedisTemplate对基础数据进行操作
 */
@RestController
@RequestMapping("/redisbase")
public class RedisBaseController {
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    RedissonClient redissonClient;
    @Autowired
    AttrDao attrDao;

    /**
     * 使用Template操作String类型的数据。
     * 一种实现方法 -- opsForValue()
     * https://docs.spring.io/spring-data/redis/docs/current/api/org/springframework/data/redis/core/ValueOperations.html
     *
     * @return
     */
    @RequestMapping("/templatevalue")
    public R template_opsForValue() {
        String redisKeyPrex = "demo:redis:";
        AttrEntity attrEntity = attrDao.selectById(2L);
        R r = R.ok();



        /**
         * void set(K,V)
         * 不携带任何额外参数。只是<k-v>
         */
        stringRedisTemplate.opsForValue().set(redisKeyPrex + "1", JSON.toJSONString(attrEntity));

        /**
         * void set(K,V,timeout,timeUnit)
         * 携带过期时间
         */
        stringRedisTemplate.opsForValue().set(redisKeyPrex + "2",
                JSON.toJSONString(attrEntity),
                10,
                TimeUnit.SECONDS);

        /**
         * Boolean setIfAbsent(K,V)
         * 不携带额外参数。只有不存在该K时才set该K
         */
        stringRedisTemplate.opsForValue().setIfAbsent(redisKeyPrex + "3", JSON.toJSONString(attrEntity));

        /**
         * Boolean setIfAbsent(K,V,timeout,TimeUnit)
         * 携带过期时间。只有不存在该K时才set该K
         */
        stringRedisTemplate.opsForValue().setIfAbsent(redisKeyPrex + "4",
                JSON.toJSONString(attrEntity),
                10,
                TimeUnit.SECONDS);

        /**
         * Boolean setIfPresent(K,V)
         * 不携带任何参数。只有该K存在才set该K
         */
        stringRedisTemplate.opsForValue().setIfPresent(redisKeyPrex + "5", JSON.toJSONString(attrEntity));

        /**
         * Boolean setIfPresent(K key, V value,long timeout,TimeUnit unit)
         * 携带过期时间。只有该K存在才set该K
         */
        stringRedisTemplate.opsForValue().setIfPresent(redisKeyPrex + "6",
                JSON.toJSONString(attrEntity),
                10,
                TimeUnit.SECONDS);

        /**
         * void multiSet(Map<? extends K,? extends V> map)
         * 多重set，传入map，每一个元素作为一个键值对
         */
        Map<String, String> map = new HashMap<>();
        map.put(redisKeyPrex + "7", JSON.toJSONString(attrEntity));
        map.put(redisKeyPrex + "8", JSON.toJSONString(attrEntity));
        stringRedisTemplate.opsForValue().multiSet(map);


        /**
         * void multiSetIfAbsent(Map<? extends K,? extends V> map)
         * 传入map，每一个元素作为一个键值对。
         * 必须map中所有的K都是不存在的才可以。
         */
        map.put(redisKeyPrex + "9", JSON.toJSONString(attrEntity));
        stringRedisTemplate.opsForValue().multiSetIfAbsent(map);

        /**
         * V get(Object key)
         * 获取K对应的V
         */
        String res_1 = stringRedisTemplate.opsForValue().get(redisKeyPrex + "1");
        r.put("res_1", JSON.parseObject(res_1, AttrEntity.class).getAttrName());  // 业务环境下要做非空判别

        /**
         * V getAndDelete(K key)
         * 获取K并删除K
         * spring-boot-starter-data-redis 2.6 之后才有
         */
        String andDelete = stringRedisTemplate.opsForValue().getAndDelete(redisKeyPrex + "1");

        /**
         * V getAndExpire(K key,long timeout,TimeUnit unit)
         * 获取K并重新设置过期时间
         * spring-boot-starter-data-redis 2.6 之后才有
         */
        String andExpire = stringRedisTemplate.opsForValue().getAndExpire(redisKeyPrex + "2", 20, TimeUnit.SECONDS);

        /**
         * V getAndPersist(K key)
         * 获取K并持久化K，删除K的一切过期时间
         * spring-boot-starter-data-redis 2.6 之后才有
         */
        String andPersist = stringRedisTemplate.opsForValue().getAndPersist(redisKeyPrex + "4");

        /**
         * V getAndSet(K key,V value)
         * 获取K的旧值，并重新设置新值
         */
        String new_value = stringRedisTemplate.opsForValue().getAndSet(redisKeyPrex + "4",
                JSON.toJSONString("new value"));

        /**
         * List<V> multiGet(Collection<K> keys)
         * 多重获取。
         * 结果List和输入Collection的size严格对应。没有命中的位置会以Null显示
         */
        List<String> multiKey = new ArrayList<>();
        multiKey.add(redisKeyPrex + "7");
        multiKey.add(redisKeyPrex + "8");
        List<String> multiGet = stringRedisTemplate.opsForValue().multiGet(multiKey);

        /**
         * Long increment(K key)
         * template提供的一种信号量锁的实现。只对Integer形式的V起作用。
         * 对V 自增1，返回自增后的值的Long形式
         */
        stringRedisTemplate.opsForValue().set(redisKeyPrex + "increment", "5");
        // 注意，如果redis中没有redisKeyPrex + "increment"这个记录，执行完下面命令后
        // 会生成  redisKeyPrex + "increment" : 1 的记录
        Long increment = stringRedisTemplate.opsForValue().increment(redisKeyPrex + "increment");

        /**
         * Long increment(K key, long delta)  /  Double increment(K key, double delta)
         * 对V自增delta，返回自增后的值的Long形式
         */
        // 同样，如果redis中没有redisKeyPrex + "increment"这个记录，执行完下面命令后
        // 会生成  redisKeyPrex + "increment" : delta 的记录
        Long increment1 = stringRedisTemplate.opsForValue().increment(redisKeyPrex + "increment", 10L);
        Double increment2 = stringRedisTemplate.opsForValue().increment(redisKeyPrex + "increment", 5.5);

        /**
         * Long decrement(K key)
         * 对 V自减1
         */
        stringRedisTemplate.opsForValue().set(redisKeyPrex + "decrement", "50");
        Long decrement = stringRedisTemplate.opsForValue().decrement(redisKeyPrex + "decrement");

        /**
         * Long decrement(K key, long delta) /
         * 对 V自减delta
         */
        Long decrement1 = stringRedisTemplate.opsForValue().decrement(redisKeyPrex + "decrement", 10L);
        // 自减不存在自减 double

        /**
         * Integer append(K key, String value)
         * 为V拼接vlaue。不建议使用，直接使用重新赋值最好
         */
        stringRedisTemplate.opsForValue().append(redisKeyPrex + "increment", "appendNew");

        /**
         * String get(K key,long start,long end)
         * 对V取 substring返回。不建议使用，全部获得后在后端进行sub
         */
        String s = stringRedisTemplate.opsForValue().get(redisKeyPrex + "increment", 2, 5);

        /**
         * void set(K key,V value,long offset)
         * 从V的offset开始覆写新的value。不建议使用，建议直接重新赋值
         */
        stringRedisTemplate.opsForValue().set(redisKeyPrex + "increment", "all_rewrite", 3);

        /**
         * Long size(K key)
         * 获取V的size
         */
        Long size = stringRedisTemplate.opsForValue().size(redisKeyPrex + "increment");
        r.put("size", size);

        return r;
    }

    /**
     * 使用Template操作String类型的数据。
     * 一种实现方法 -- boundValueOps(K)
     * https://docs.spring.io/spring-data/redis/docs/current/api/org/springframework/data/redis/core/BoundValueOperations.html
     *
     * @return
     */
    @RequestMapping("/templateboundvalue")
    public R template_boundValue() {
        R r = R.ok();
        String redisKeyPrex = "demo:redis:";
        // 不必一定要存在这个Key，现在只是先绑定这个Key的值
        // 使用 boundValueOps()时会绑定唯一的key，因此在 opsForValue()中所有的multi操作这里都没有
        BoundValueOperations<String, String> boundValueOperations =
                stringRedisTemplate.boundValueOps(redisKeyPrex + "bound");

        /**
         * void set(V value)
         * 设置V
         */
        boundValueOperations.set("value1");
        boundValueOperations.set("value2", 20, TimeUnit.SECONDS);
        boundValueOperations.setIfAbsent("value3");
        boundValueOperations.setIfAbsent("value4", 20, TimeUnit.SECONDS);
        boundValueOperations.setIfPresent("value5");
        boundValueOperations.setIfPresent("value6", 20, TimeUnit.SECONDS);

        /**
         * 获取绑定的K的过期时间
         */
        Long expire = boundValueOperations.getExpire();

        /**
         * 无数据模式下，设置绑定的K的过期时间
         */
        boundValueOperations.expire(10, TimeUnit.SECONDS);

        /**
         * 获取绑定的K的键名
         */
        String key = boundValueOperations.getKey();

        /**
         * V get()
         * 获取V
         */
        boundValueOperations.set("value7");
        String s = boundValueOperations.get();
        String andExpire = boundValueOperations.getAndExpire(20, TimeUnit.SECONDS);
        String andPersist = boundValueOperations.getAndPersist();
        boundValueOperations.getAndSet("value8");
        String andDelete = boundValueOperations.getAndDelete();

        /**
         * Long increment()
         * 自增
         */
        boundValueOperations.set("10");
        Long increment = boundValueOperations.increment();
        Long increment1 = boundValueOperations.increment(10L);
        Double increment2 = boundValueOperations.increment(5.5);

        /**
         * Long decrement()
         * 自减
         */
        boundValueOperations.set("10");
        Long decrement = boundValueOperations.decrement();
        Long decrement1 = boundValueOperations.decrement(3L);

        /**
         * others
         */
        boundValueOperations.set("otherOppreation");
        boundValueOperations.append("Append");
        String append = boundValueOperations.get(2, 10);
        boundValueOperations.set("rewrite", 6);
        Long size = boundValueOperations.size();

        return r;
    }

    /**
     * 使用Template操作HashMap类型的数据
     * 一种实现方法 -- opsForHash()
     * https://docs.spring.io/spring-data/redis/docs/current/api/org/springframework/data/redis/core/HashOperations.html
     *
     * @return
     */
    @RequestMapping("/templatehash")
    public R template_opsForHash() {
        R r = R.ok();
        AttrEntity attrEntity = attrDao.selectById(2L);
        String redisKeyPrex = "demo:redis:hashMap:";
        String mapKeyPrex = "index:";

        /**
         * void put(H key,HK hashKey,HV value)
         * 向 H 哈希表中，存放一个 哈希键值对
         */
        stringRedisTemplate.opsForHash().put(redisKeyPrex + "1",
                mapKeyPrex + "1", "23");

        /**
         * Boolean putIfAbsent(H key,HK hashKey,HV value)
         * 注意，是当 哈希键不存在时才set，而不是H哈希表不存在时set
         */
        stringRedisTemplate.opsForHash().putIfAbsent(redisKeyPrex + "1",
                mapKeyPrex + "1", "6.137");

        /**
         * void putAll(H key,Map<? extends HK,? extends HV> m)
         * 向 H 哈希表中存放多个 哈希键值对
         */
        Map<String, String> map = new HashMap<>();
        map.put(mapKeyPrex + "2", JSON.toJSONString(attrEntity));
        map.put(mapKeyPrex + "3", "StringValue");
        stringRedisTemplate.opsForHash().putAll(redisKeyPrex + "1", map);

        /**
         * 上面三个put可以很明显的看出redis的一个优点:
         *   由于redis本质上都是用字符串存放数据，所以后端的任何数据类型，只要做了toString，都可以往redis放。
         *   完全不用像在java中使用HashMap一样，考虑数据兼容性问题。
         *   反正都转成String类型的数据了，在redis看来就是全部都是String
         */

        /**
         * Long delete(H key, Object... hashKeys)
         * 删除 H 哈希表中的指定哈希key
         * 也可以构建list后，使用.toArrays传入
         */
        stringRedisTemplate.opsForHash().delete(redisKeyPrex + "1",
                mapKeyPrex + "1", mapKeyPrex + "3");

        /**
         * Boolean hasKey(H key, Object hashKey)
         * 判断 H 哈希表中是否存在指定哈希key
         */
        Boolean hasKey = stringRedisTemplate.opsForHash().hasKey(redisKeyPrex + "1", mapKeyPrex + "1");

        /**
         * HV get(H key,Object hashKey)
         * 从H哈希表中获取指定哈希Key的值
         * 注意返回值是Object，并不强制是String。但实际上就是String
         */
        Object hashGet = stringRedisTemplate.opsForHash().get(redisKeyPrex + "1", mapKeyPrex + "2");
        AttrEntity attrGet = JSON.parseObject((String) hashGet, AttrEntity.class);
        r.put("attrGet", attrGet);

        /**
         * List<HV> multiGet(H key,Collection<HK> hashKeys)
         * 从H哈希表中获取指定的多个哈希Key的值
         * 同样返回List的元素是Object
         * 结果List和输入Collection的size严格对应。没有命中的位置会以Null显示
         */
        // 注意哈希key列表也应该是Object类型
        List<Object> hashKeyList = new ArrayList<>();
        hashKeyList.add(mapKeyPrex + "1");
        hashKeyList.add(mapKeyPrex + "2");
        List<Object> multiGet = stringRedisTemplate.opsForHash().multiGet(redisKeyPrex + "1", hashKeyList);


        /**
         * Long increment(H key,HK hashKey,long delta) / Double increment(H key,HK hashKey,double delta)
         * 无自增1，也无decrement
         * 同样只对Integer起作用
         */
        // 同样如果无记录，则执行完成后生成新纪录
        Long increment = stringRedisTemplate.opsForHash().increment(redisKeyPrex + "1", mapKeyPrex + "1", 5L);
        Double increment1 = stringRedisTemplate.opsForHash().increment(redisKeyPrex + "1", mapKeyPrex + "1", 5.25);

        /**
         * Set<HK> keys(H key)
         * 返回H 哈希表中的所有 哈希键set集合
         * 注意返回的key 都是在H下的哈希键，并不携带H的键名
         */
        Set<Object> keys = stringRedisTemplate.opsForHash().keys(redisKeyPrex + "1");

        /**
         * List<HK> randomKeys(H key, long count)
         * 随机返回count个key。如果这个hash是一个非常庞大的对象，使用这种方法分批删除
         */
        List<Object> randomKeys = stringRedisTemplate.opsForHash().randomKeys(redisKeyPrex + "1", 50);
        stringRedisTemplate.opsForHash().delete(redisKeyPrex + "1", randomKeys);

        /**
         * Map<HK,HV> entries(H key)
         * 将 H 哈希表 下的所有哈希键值对 以 Map的形式返回
         */
        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(redisKeyPrex + "1");

        /**
         * Long lengthOfValue(H key,HK hashKey)
         * 返回 H 哈希表中指定哈希键的值长度
         */
        stringRedisTemplate.opsForHash().lengthOfValue(redisKeyPrex + "1", mapKeyPrex + "1");

        /**
         * Long size(H key)
         * 返回 H 哈希表中哈希键的数量
         */
        stringRedisTemplate.opsForHash().size(redisKeyPrex + "1");

        return r;
    }

    /**
     * 使用Template操作HashMap类型的数据
     * 一种实现方法 -- boundHashOps(K key)
     * https://docs.spring.io/spring-data/redis/docs/current/api/org/springframework/data/redis/core/BoundHashOperations.html
     *
     * @return
     */
    @RequestMapping("templateboundhash")
    public R template_boundHash() {
        R r = R.ok();
        AttrEntity attrEntity = attrDao.selectById(2L);
        String redisKeyPrex = "demo:redis:hashMap:";
        String mapKeyPrex = "index:";

        /**
         * 绑定一个 H 哈希表
         * 原始API是 <String, Object, Object>
         * 可以直接改成 <String, String, String>, 只要做好数据的toString即可
         * 这里是和 opsForHash 不用的，opsForHash只能选择Object，拿到之后再转类型。但这里可以直接选择String
         */
        BoundHashOperations<String, String, String> boundHashOperations =
                stringRedisTemplate.boundHashOps(redisKeyPrex + "1");

        /**
         * put
         */
        boundHashOperations.put(mapKeyPrex + "1", "23");

        /**
         * putIfAbsent
         */
        boundHashOperations.putIfAbsent(mapKeyPrex + "1", "6.137");

        /**
         * putAll
         */
        Map<String, String> map = new HashMap<>();
        map.put(mapKeyPrex + "2", JSON.toJSONString(attrEntity));
        map.put(mapKeyPrex + "3", "StringValue");
        boundHashOperations.putAll(map);

        /**
         * delete
         */
        boundHashOperations.delete(mapKeyPrex + "1", mapKeyPrex + "3");

        /**
         * hasKey
         */
        Boolean hasKey = boundHashOperations.hasKey(mapKeyPrex + "1");

        /**
         * get
         * 直接得到就是 String 而非 Object
         */
        String hashGet = boundHashOperations.get(mapKeyPrex + "2");
        AttrEntity attrGet = JSON.parseObject(hashGet, AttrEntity.class);

        /**
         * multiGet
         * 直接得到就是 String 而非 Object
         */
        List<String> hashKeyList = new ArrayList<>();
        hashKeyList.add(mapKeyPrex + "1");
        hashKeyList.add(mapKeyPrex + "2");
        List<String> multiGet = boundHashOperations.multiGet(hashKeyList);

        /**
         * increment
         */
        boundHashOperations.put(mapKeyPrex + "1", "10");
        Long increment = boundHashOperations.increment(mapKeyPrex + "1", 2L);
        Double increment1 = boundHashOperations.increment(mapKeyPrex + "1", 2.4);

        /**
         * keys
         * 直接返回String
         */
        Set<String> keys = boundHashOperations.keys();

        /**
         * randomKeys
         * 随机返回keys
         */
        List<String> randomKeys = boundHashOperations.randomKeys(50);
        boundHashOperations.delete(randomKeys);


        /**
         * size
         */
        Long size = boundHashOperations.size();

        /**
         * values
         * 直接返回String
         */
        List<String> values = boundHashOperations.values();

        /**
         * entries
         * 直接返回 <String,String>
         */
        Map<String, String> entries = boundHashOperations.entries();

        return r;
    }

    /**
     * 使用Template操作List
     * 一种实现方法--opsForList()
     * https://docs.spring.io/spring-data/redis/docs/current/api/org/springframework/data/redis/core/ListOperations.html
     * <p>
     * redis的list原生就是双端list
     *
     * @return
     */
    @RequestMapping("templatelist")
    public R tepmlate_opsForList() {
        R r = R.ok();
        AttrEntity attrEntity = attrDao.selectById(2L);
        String redisKeyPrex = "demo:redis:list:";

        /**
         * Long leftPush(K key,V value)
         * 向 K列表左端 推入一个 V
         * 列表左端是下标0
         */
        stringRedisTemplate.opsForList().leftPush(redisKeyPrex + "1", "10");

        /**
         * Long leftPushAll(K key,V... values)
         * 向 K列表左端 推入多个 V
         * 列表左端是下标0，函数输入的第一个V最先被推入
         */
        stringRedisTemplate.opsForList().leftPush(redisKeyPrex + "1", JSON.toJSONString(attrEntity));
        stringRedisTemplate.opsForList().leftPush(redisKeyPrex + "1", "ThirdElement");

        /**
         * Long leftPushAll(K key,Collection<V> values)
         * 向 K 列表左端 推入一个Collection
         * Collection中下标为0的元素第一个被推入
         */
        List<String> valList = new ArrayList<>();
        valList.add("FourthElement");
        valList.add("FifthElement");
        stringRedisTemplate.opsForList().leftPushAll(redisKeyPrex + "1", valList);

        /**
         * Long leftPushIfPresent(K key, V value)
         * 如果K存在，才向 K列表的左端推入一个V
         */
        stringRedisTemplate.opsForList().leftPushIfPresent(redisKeyPrex + "1", "SixthElement");

        /**
         * Long leftPush(K key,V pivot,V value)
         * 在 K列表中 查找 pivot， 然后向pivot的左边即下标0的方向，插入value
         * 查找的pivot值不能在list中重复出现
         */
        stringRedisTemplate.opsForList().leftPush(redisKeyPrex + "1", "ThirdElement", "InsertElement");

        /**
         * Long rightPush(K key,V value)
         * 向 K列表右端 推入一个 V
         * 列表右端下标是 size-1
         */
        stringRedisTemplate.opsForList().rightPush(redisKeyPrex + "1", "10");

        /**
         * Long rightPushAll(K key,V... values)
         * 向 K列表右端 推入多个 V
         */
        stringRedisTemplate.opsForList().rightPush(redisKeyPrex + "1", JSON.toJSONString(attrEntity));
        stringRedisTemplate.opsForList().rightPush(redisKeyPrex + "1", "ThirdElement_R");

        /**
         * Long rightPushAll(K key,Collection<V> values)
         * 向 K 列表右端 推入一个Collection
         * Collection中下标为0的元素第一个被推入
         */
        valList = new ArrayList<>();
        valList.add("FourthElement");
        valList.add("FifthElement");
        stringRedisTemplate.opsForList().rightPushAll(redisKeyPrex + "1", valList);

        /**
         * Long rightPushIfPresent(K key, V value)
         * 如果K存在，才向 K列表的右端推入一个V
         */
        stringRedisTemplate.opsForList().rightPushIfPresent(redisKeyPrex + "1", "SixthElement");

        /**
         * Long rightPush(K key,V pivot,V value)
         * 在 K列表中 查找 pivot， 然后向pivot的右边即下标size-1的方向，插入value
         * 查找的pivot值不能在list中重复出现
         */
        stringRedisTemplate.opsForList().rightPush(redisKeyPrex + "1", "ThirdElement_R", "InsertElement");

        /**
         * List<V> range(K key,long start,long end)
         * 获得K列表下标 start-end 的内容
         * start 和 end 的内容会被包含进来
         */
        List<String> range = stringRedisTemplate.opsForList().range(redisKeyPrex + "1", 2, 5);

        /**
         * void trim(K key, long start, long end)
         * 将 K 列表下标 start-end 的内容提取出来，覆盖掉K的原始内容
         */
        stringRedisTemplate.opsForList().trim(redisKeyPrex + "1", 2, 5);

        /**
         * Long size(K key)
         * 获取 K 列表的size
         */
        Long size = stringRedisTemplate.opsForList().size(redisKeyPrex + "1");

        /**
         * void set(K key, long index, V value)
         * 设置 K 列表下标为index的值为新值
         */
        stringRedisTemplate.opsForList().set(redisKeyPrex + "1", 0, "newValue");

        /**
         * Long remove(K key, long count, Object value)
         * 从下标0开始寻找所有等于 value的值，然后将其删除前count个
         */
        stringRedisTemplate.opsForList().remove(redisKeyPrex + "1", 2, "InsertElement");

        /**
         * V index(K key, long index)
         * 返回下标为 index的值.index从0开始算
         */
        String index = stringRedisTemplate.opsForList().index(redisKeyPrex + "1", 1L);

        /**
         * Long indexOf(K key, V value)
         * 从下标0开始寻找所有等于 value的值，返回第一个的下标
         */
        stringRedisTemplate.opsForList().rightPush(redisKeyPrex + "1", "ThirdElement");
        Long firstIndexOf = stringRedisTemplate.opsForList().indexOf(redisKeyPrex + "1", "ThirdElement");

        /**
         * Long lastIndexOf(K key, V value)
         * 从下标0开始寻找所有等于 value的值，返回最后一个的下标
         */
        Long lastIndexOf = stringRedisTemplate.opsForList().lastIndexOf(redisKeyPrex + "1", "ThirdElement");

        /**
         * V leftPop(K key)
         * 从下标为0的位置弹出一个值，并从列表中删除
         */
        stringRedisTemplate.opsForList().leftPush(redisKeyPrex + "1", "newValue1");
        stringRedisTemplate.opsForList().leftPush(redisKeyPrex + "1", "newValue2");
        String leftPop = stringRedisTemplate.opsForList().leftPop(redisKeyPrex + "1");

        /**
         * List<V> leftPop(K key, long count)
         * 从下标为0的位置弹出count个值，并从列表中删除
         */
        List<String> leftPopCount = stringRedisTemplate.opsForList().leftPop(redisKeyPrex + "1", 2L);

        /**
         * V rightPop(K key)
         * List<V> rightPop(K key, long count)
         * 从列表右边进行Pop
         */

        /**
         * V rightPopAndLeftPush(K sourceKey, K destinationKey)
         * 从 sourceKey列表的右端Pop一个元素，然后从destinationKey列表的左端push进去
         */
        stringRedisTemplate.opsForList().rightPopAndLeftPush(redisKeyPrex + "1", redisKeyPrex + "2");

        RedisConnectionUtils.unbindConnection(stringRedisTemplate.getConnectionFactory());

        return r;
    }

    /**
     * 使用Template操作List类型的数据
     * 一种实现方法 -- boundListOps(K key)
     * https://docs.spring.io/spring-data/redis/docs/current/api/org/springframework/data/redis/core/BoundListOperations.html
     * *
     *
     * @return
     */
    @RequestMapping("/templateboundlist")
    public R template_boundList() {
        R r = R.ok();
        AttrEntity attrEntity = attrDao.selectById(2L);
        String redisKeyPrex = "demo:redis:list:";

        /**
         * 绑定之后不再赘述，方法名几乎相同，只是不用再写列表键名了
         */
        BoundListOperations<String, String> boundListOperations =
                stringRedisTemplate.boundListOps(redisKeyPrex + "1");

        return r;
    }

    /**
     * 使用template操作set类型的数据
     * 一种实现方法 opsForValueSet
     * https://docs.spring.io/spring-data/redis/docs/current/api/org/springframework/data/redis/core/SetOperations.html
     *
     * @return
     */
    @RequestMapping("/templateset")
    public R template_set() {
        R r = R.ok();
        AttrEntity attrEntity = attrDao.selectById(2L);
        String redisKeyPrex = "demo:redis:set:";

        /**
         * Set中的元素是无序的且不允许重复的。
         * Set不存在下标的概念，无法使用下标获取元素
         */

        /**
         * Long add(K key, V... values)
         * 向 K set中添加给定的value
         */
        stringRedisTemplate.opsForSet().add(redisKeyPrex + "1",
                "10", "newValue", JSON.toJSONString(attrEntity));

        /**
         * Long remove(K key, Object... values)
         * 从 K set中删除与指定值相同的value
         */
        stringRedisTemplate.opsForSet().remove(redisKeyPrex + "1", "newValue");

        /**
         * V pop(K key)
         * 从K set中 随机 Pop出一个元素
         */
        String pop = stringRedisTemplate.opsForSet().pop(redisKeyPrex + "1");

        /**
         * List<V> pop(K key, long count)
         * 从 K set中 随机 Pop出 count 个元素
         * List<String> popList = stringRedisTemplate.opsForSet().pop(redisKeyPrex + "1", 3);
         */

        /**
         * Boolean move(K key, V value, K destKey)
         * 将 key Set中的Vlaue值移动到 destKey Set中
         * Boolean move = stringRedisTemplate.opsForSet().move(redisKeyPrex + "1", "newValue", redisKeyPrex + "2");
         */

        /**
         * Long size(K key)
         * 获取 key Set的size
         */
        Long size = stringRedisTemplate.opsForSet().size(redisKeyPrex + "1");

        /**
         * Boolean isMember(K key, Object o)
         * 判断一个元素是否位于 K set中
         */
        stringRedisTemplate.opsForSet().isMember(redisKeyPrex + "1", "isValue?");

        /**
         * Set<V> intersect(K key, K otherKey)
         * 对两个Set做交集运算，返回运算结果
         * Set<String> intersect = stringRedisTemplate.opsForSet().intersect(redisKeyPrex + "1", redisKeyPrex + "2");
         * Set<V> intersect(K key, K otherKey)
         * 对多个Set做交集
         */

        /**
         * Long intersectAndStore(K key, K otherKey, K destKey)
         * 对 key 和 otherKey做交集，将结果保存在 destKey中
         * Set<String> intersect = stringRedisTemplate.opsForSet().intersect(redisKeyPrex + "1", redisKeyPrex + "2", redisKetPrex + "3");
         * Long intersectAndStore(K key, Collection<K> otherKeys, K destKey)
         * 对 key和多个其他set做交集，结果保存在 destKey 中
         */

        /**
         * Set<V> union(K key, K otherKey)
         * 对两个set做并集，返回操作结果
         * Set<String> union = stringRedisTemplate.opsForSet().union(redisKeyPrex + "1", redisKeyPrex + "2");
         * Set<V> union(K key, Collection<K> otherKeys)
         * 对多个set做并集，返回操作结果
         */

        /**
         * Long unionAndStore(K key, K otherKey, K destKey)
         * Long unionAndStore(K key, Collection<K> otherKeys, K destKey)
         */

        /**
         * Set<V> difference(K key, K otherKey)
         * 差集
         */

        /**
         * Set<V> members(K key)
         * 返回Kset中所有的元素
         */
        Set<String> members = stringRedisTemplate.opsForSet().members(redisKeyPrex + "1");


        return r;
    }

    /**
     * 使用template操作set类型的数据
     * 一种实现方法 boundSetOps(K)
     * https://docs.spring.io/spring-data/redis/docs/current/api/org/springframework/data/redis/core/BoundSetOperations.html
     * @return
     */
    @RequestMapping("templateboundset")
    public R template_boundSet(){
        R r = R.ok();
        AttrEntity attrEntity = attrDao.selectById(2L);
        String redisKeyPrex = "demo:redis:set:";

        /**
         * 大同小异，不再赘述
         */
        BoundSetOperations<String, String> boundSetOperations = stringRedisTemplate.boundSetOps(redisKeyPrex + "1");
        return r;
    }

    /**
     * 从2.2版本之后提供的新基础数据类型--地理位置信息
     * 操作方法都一样，只是需要学习一种redis独有的地理数据 Point和GeoLocation
     * 暂时跳过
     * @return
     */
    @RequestMapping("templateGEO")
    public R template_GEO(){
        R r = R.ok();
        AttrEntity attrEntity = attrDao.selectById(2L);
        String redisKeyPrex = "demo:redis:geo:";
        return r;
    }


    @RequestMapping("/test")
    public R test(){
        BoundHashOperations<String, String, String> yyyy = stringRedisTemplate.boundHashOps("yyyy");
        yyyy.put("1","wangwu");
        yyyy.put("2","wangwu");
        yyyy.put("3","wangwu");
        yyyy.put("4","wangwu");

        ArrayList<String> l = new ArrayList<>();
        l.add("1");
        l.add("5");

        yyyy.delete(l.toArray());

        return R.ok();
    }



}
